/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gui.panels;

import data.logic.Square;
import data.logic.VillageSquareType;
import data.logic.WorldLogic;
import data.*;
import data.logic.BuildingSquareTypeFactory;
import gui.AbstrWorld;
import gui.ChooseVillage;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.*;
import javax.swing.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import gui.MaiaWorld;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import maiaclient.interfaces.IVillageListener;

/**
 *
 * @author groep 11
 * @date 2007-2008
 */
public class WorldMap extends JPanel implements MouseMotionListener, MouseListener, IVillageListener {

    private VillageInfo pnlVillageInfo;
    private WorldLogic world;
    private Point temp = null;
    Point startpoint = new Point();
    final String NATUREXML = "src/data/xml/nature.xml";
    private AbstrWorld parent;

    public WorldMap(AbstrWorld parent) {

        this.parent = parent;
        init();
    }

    public void init() {

        setWorld(new WorldLogic());

        loadRiver();
        loadForest();
        loadVillages();


        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        this.setBackground(Color.GRAY);

    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Square[][] squares = world.getSquares();
        int i = 0, j = 0;
        int w = calculateSize();

        startpoint.x = (int) (this.getSize().getWidth() - w * (int) world.getDimension().getWidth()) / 2;
        startpoint.y = (int) (this.getSize().getHeight() - w * (int) world.getDimension().getHeight()) / 2;

        for (Square[] rij : squares) {
            for (Square v : rij) {
                if (v.isHighlighted()) {
                    g.setColor(Color.ORANGE);
                } else {
                    g.setColor(v.getSquareType().getColor());
                }
                if (v.getSquareType() instanceof VillageSquareType) {
                    try {
                        BufferedImage source = ImageIO.read(new File(v.getSquareType().getImageUrl()));
                        BufferedImage dest = new BufferedImage(w, w, BufferedImage.TYPE_INT_RGB);
                        Graphics2D g2 = dest.createGraphics();
                        g2.drawImage(source, 0, 0, w, w, null);

                        g.drawImage(dest, i * w + startpoint.x, j * w + startpoint.y, null);
                    } catch (IOException ex) {
                        Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    g.fillRect(i * w + startpoint.x, j * w + startpoint.y, w, w);
                }

                i++;
            }
            i = 0;
            j++;
        }
    }

    public void loadRiver() {

        Vector<Square> river = this.getRiver();
        world.drawRiver(river);
    }

    public void loadForest() {

        Vector<Square> forest = this.getForest();
        world.drawForest(forest);
    }

    public void loadVillages() {

        if (parent instanceof ChooseVillage) {

            Thread t = new Thread() {

                @Override
                public void run() {
                    try {
                        parent.getMaia().getMaiaClient().connect();
                        Vector<Square> villages = parent.getMaia().fetchInitializedVillages();
                        world.drawVillages(villages);
                        parent.getMaia().getMaiaClient().closeConnection();
                    } catch (IOException ex) {
                        Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (ClassNotFoundException ex) {
                        Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            };
            t.start();
        } else {
            parent.getMaia().addVillageListener(this);
            try {
                parent.getMaia().requestInitializedVillages();

            } catch (IOException ex) {
                Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public Vector<Square> getRiver() {
        Vector<Square> river = new Vector<Square>();
        try {

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new File(NATUREXML));

            NodeList nlRivers = document.getElementsByTagName("river");

            Node nRiver = nlRivers.item(0);
            NodeList nlSquares = nRiver.getChildNodes();
            for (int countSquares = 0; countSquares < nlSquares.getLength(); countSquares++) {
                Node nSquare = nlSquares.item(countSquares);
                int YValue = 0;
                int XValue = 0;
                NodeList nlXY = nSquare.getChildNodes();
                for (int xy = 0; xy < nlXY.getLength(); xy++) {
                    Node NodeXY = nlXY.item(xy);
                    if (NodeXY.getNodeName().equals("y")) {
                        YValue = Integer.parseInt(NodeXY.getFirstChild().getNodeValue());
                    }
                    if (NodeXY.getNodeName().equals("x")) {
                        XValue = Integer.parseInt(NodeXY.getFirstChild().getNodeValue());
                    }
                }
                if (nSquare.getNodeName().equals("square")) {
                    river.add(new Square(null, new Point(YValue, XValue)));
                }
            }
        } catch (SAXException ex) {
            Logger.getLogger(MaiaWorld.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(MaiaWorld.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(MaiaWorld.class.getName()).log(Level.SEVERE, null, ex);
        }
        return river;
    }

    public Vector<Square> getForest() {
        Vector<Square> forest = new Vector<Square>();
        try {

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new File(NATUREXML));

            NodeList nlForest = document.getElementsByTagName("forest");

            Node nForest = nlForest.item(0);
            NodeList nlSquares = nForest.getChildNodes();
            for (int countSquares = 0; countSquares < nlSquares.getLength(); countSquares++) {
                Node nSquare = nlSquares.item(countSquares);
                int YValue = 0;
                int XValue = 0;
                NodeList nlXY = nSquare.getChildNodes();
                for (int xy = 0; xy < nlXY.getLength(); xy++) {
                    Node NodeXY = nlXY.item(xy);
                    if (NodeXY.getNodeName().equals("y")) {
                        YValue = Integer.parseInt(NodeXY.getFirstChild().getNodeValue());
                    }
                    if (NodeXY.getNodeName().equals("x")) {
                        XValue = Integer.parseInt(NodeXY.getFirstChild().getNodeValue());
                    }
                }
                if (nSquare.getNodeName().equals("square")) {
                    forest.add(new Square(null, new Point(YValue, XValue)));
                }
            }
        } catch (SAXException ex) {
            Logger.getLogger(MaiaWorld.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(MaiaWorld.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(MaiaWorld.class.getName()).log(Level.SEVERE, null, ex);
        }
        return forest;
    }

    private int calculateSize() {
        int w = this.getHeight();
        if (w > this.getWidth()) {
            w = this.getWidth();
        }
        return (int) (w / getWorld().getDimension().getHeight());
    }

    public void setVillageLocationAssignDefaultResourcesAndSetTownCenter(Point location) {
        try {

            parent.getMaia().getMaiaClient().connect();

            // Write Village Location to database
            parent.getMaia().updateVillageLocation(location);
            // Write default resources to database
            parent.getMaia().assignDefaultResources();
            //First set BuildingSquareTypeFactory with all BuildingTypes
            BuildingSquareTypeFactory.getInstance().setBuildingTypes(parent.getMaia().fetchBuildingTypes());
            // Write Town center to database
            parent.getMaia().assignTownCenter();

            parent.getMaia().getMaiaClient().closeConnection();

        } catch (IOException ex) {
            Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }

    public void loadDorpInfo(VillageSquareType selectedVillage) {

        String sVillageName = selectedVillage.getVillage().getName();
        int iUserId = selectedVillage.getVillage().getOwnerId();
        User owner = null;

        if (parent instanceof ChooseVillage) {
            try {
                parent.getMaia().getMaiaClient().connect();
                owner = parent.getMaia().fetchUserByUserId(iUserId);
                parent.getMaia().getMaiaClient().closeConnection();

                pnlVillageInfo.setSVillageName(sVillageName);
                pnlVillageInfo.updateUser(owner);

            } catch (IOException ex) {
                Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            try {
                parent.getMaia().requestUserByUserId(iUserId);
                pnlVillageInfo.setSVillageName(sVillageName);
            } catch (IOException ex) {
                Logger.getLogger(WorldMap.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    public void updateVillages(Vector<Square> villages) {

        world.drawVillages(villages);
        this.repaint();

    }


    //<editor-fold defaultstate="collapsed" desc="Implementation MouseListeners">   
    public void mouseDragged(MouseEvent e) {
    //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void mouseMoved(MouseEvent e) {

        int w = calculateSize();

        int i = (int) Math.floor((e.getX() - startpoint.x) / w);
        int j = (int) Math.floor((e.getY() - startpoint.y) / w);
        // We can only do things if our cursor is over the WorldLogic
        if ((i < getWorld().getDimension().getWidth()) && i >= 0 && j >= 0 &&
                (j < getWorld().getDimension().getHeight()) && e.getX() > startpoint.x && e.getY() > startpoint.y) {

            //If we go over a "Village" cursors changes into a hand
            if (world.getSquares()[j][i].getSquareType() instanceof VillageSquareType) {
                setCursor(new Cursor(Cursor.HAND_CURSOR));
            } else {
                setCursor(new Cursor((Cursor.DEFAULT_CURSOR)));
            }

            if (parent instanceof ChooseVillage) {
                if (temp != null) {
                    if (temp.x != i || temp.y != j) {
                        world.getSquares()[temp.y][temp.x].setHighlighted(false);
                    }
                }
                if (world.getSquares()[j][i].getSquareType().isBuildingPossible()) {
                    world.getSquares()[j][i].setHighlighted(true);
                    temp = new Point(i, j);
                }
            }

        } else {
            // if we leave our world with mouse we have to delete highlight too
            if (temp != null) {
                world.getSquares()[temp.y][temp.x].setHighlighted(false);
            }
        }
        this.repaint();
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
        int w = calculateSize();
        int i = (int) Math.floor((e.getX() - startpoint.x) / w);
        int j = (int) Math.floor((e.getY() - startpoint.y) / w);

        // We can only do things if our cursor is over the WorldLogic
        if ((i < getWorld().getDimension().getWidth()) && i >= 0 && j >= 0 &&
                (j < getWorld().getDimension().getHeight()) && e.getX() > startpoint.x && e.getY() > startpoint.y) {

            // If we click on a village, we need to set the Village Info
            if (world.getSquares()[j][i].getSquareType() instanceof VillageSquareType) {
                VillageSquareType selectedVillage = (VillageSquareType) world.getSquares()[j][i].getSquareType();
                loadDorpInfo(selectedVillage);
            }
            if (parent instanceof ChooseVillage) {
                // If we click an empty square we can build our village there
                if (world.getSquares()[j][i].isHighlighted()) {
                    if (JOptionPane.showConfirmDialog(this, "You are about to build your village here. " +
                            "You can't change it later.\n Are you sure you want to build it here? ",
                            "Build Village?", JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION) {

                        world.getSquares()[temp.y][temp.x].setHighlighted(false);
                        world.getSquares()[temp.y][temp.x].setSquareType(new VillageSquareType());
                        //If user has selected a village: update Location, give default resources and set Town center
                        setVillageLocationAssignDefaultResourcesAndSetTownCenter(new Point(j, i));
                        // Load MaiaWorld
                        MaiaWorld frmMaiaWorld = new MaiaWorld(parent.getMaia());
                        frmMaiaWorld.setVisible(true);
                        parent.dispose();
                    }
                }
            }
        }
    }

    public void mouseEntered(MouseEvent e) {
    //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void mouseExited(MouseEvent e) {
    //throw new UnsupportedOperationException("Not supported yet.");
    }
    //</editor-fold>
    // <editor-fold defaultstate="collapsed" desc="Getters/Setters">     
    public WorldLogic getWorld() {
        return world;
    }

    public void setWorld(WorldLogic world) {
        this.world = world;
    }

    public VillageInfo getPnlVillageInfo() {
        return pnlVillageInfo;
    }

    public void setPnlVillageInfo(VillageInfo pnlVillageInfo) {
        this.pnlVillageInfo = pnlVillageInfo;
    }

    public WorldMap getWorldMap() {
        return this;
    }

// </editor-fold>          
}
